package org.aceor.mddal.gateway.router.handler;

import com.google.common.base.Objects;
import org.aceor.mddal.common.sqlparser.result.context.ConditionContext;
import org.aceor.mddal.common.sqlparser.result.context.RouteContext;
import org.aceor.mddal.common.sqlparser.result.context.ShardingContext;
import org.aceor.mddal.common.sqlparser.result.object.Column;
import org.aceor.mddal.common.sqlparser.result.object.Table;
import org.aceor.mddal.gateway.executor.ExecuteContext;
import org.aceor.mddal.gateway.router.RouteHelper;
import org.apache.commons.collections.CollectionUtils;

import java.util.Set;

import static org.aceor.mddal.common.utils.CommonUtil.print;

/**
 * Created by lxue on 16/4/14.
 */
public class TBDBShardingRouteHandler implements RouteHandler {
    @Override
    public ExecuteContext route(RouteHandlerParam param) {
        ExecuteContext executeContext = new ExecuteContext();

        ConditionContext conditionContext = param.sqlParsedResult.conditionContext;
        RouteContext routeContext = param.sqlParsedResult.routeContext;
        ShardingContext shardingContext = param.sqlParsedResult.shardingContext;

        /**
         * 4.1 分库分表相同字段
         */
        if (Objects.equal(shardingContext.tbShardingColumn, shardingContext.dbShardingColumn)) {
            return doRouteWhenTbDbHasSameColumn(executeContext, param, routeContext, shardingContext, conditionContext);
        }
        /**
         * 4.2 分库分表不同字段
         */
        else {
            return doRouteWhenTbDbHasNotSameColumn(executeContext, param, routeContext, shardingContext, conditionContext);
        }
    }

    private ExecuteContext doRouteWhenTbDbHasSameColumn(ExecuteContext executeContext, RouteHandlerParam param, RouteContext routeContext, ShardingContext shardingContext, ConditionContext conditionContext) {
        Column column = new Column(RouteHelper.findTable(routeContext, shardingContext.logicTableName), shardingContext.tbShardingColumn);
        Set<Object> values = RouteHelper.getAcceptableValues(
                conditionContext.getConditionMap().get(column),
                shardingContext.tbShardingFunction);

        if (CollectionUtils.isEmpty(values)) {
            //TODO: 可以解决的
            throw new RuntimeException("咱不支持全表查询");
        }

        RouteHelper.setExecuteSqlMapWhenTbAndDbSameSharding(
                executeContext,
                column,
                values,
                shardingContext,
                routeContext.getSqlBuilder(),
                param.args);
        return executeContext;
    }

    private ExecuteContext doRouteWhenTbDbHasNotSameColumn(ExecuteContext executeContext, RouteHandlerParam param, RouteContext routeContext, ShardingContext shardingContext, ConditionContext conditionContext) {
        Table table = RouteHelper.findTable(routeContext, shardingContext.logicTableName);
        Column tbColumn = new Column(table, shardingContext.tbShardingColumn);
        Set<Object> tbValues = RouteHelper.getAcceptableValues(
                conditionContext.getConditionMap().get(tbColumn),
                shardingContext.tbShardingFunction);

        Column dbColumn = new Column(table, shardingContext.dbShardingColumn);
        print(TBDBShardingRouteHandler.class, "db-column:" + dbColumn);
        Set<Object> dBValues = RouteHelper.getAcceptableValues(
                conditionContext.getConditionMap().get(dbColumn),
                shardingContext.dbShardingFunction);
        if (CollectionUtils.isEmpty(dBValues)
                && CollectionUtils.isEmpty(tbValues)) {
            //TODO: 可以解决的
            throw new RuntimeException("咱不支持全表查询");
        }
        if (CollectionUtils.isEmpty(dBValues)) {
            //只分表
            RouteHelper.setExecuteSqlMapWhenSingleTbOrDbSharding(
                    executeContext,
                    tbColumn,
                    tbValues,
                    shardingContext,
                    routeContext.getSqlBuilder(),
                    param.args,
                    param.defaultDbIndex);
        } else {
            RouteHelper.setExecuteSqlMapWhenTbAndDbNotSameSharding(
                    executeContext,
                    tbColumn,
                    dbColumn,
                    tbValues,
                    dBValues,
                    shardingContext,
                    routeContext.getSqlBuilder(),
                    param.args);
        }
        return executeContext;
    }
}
